package com.btpn.custody.bean;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import javax.annotation.PostConstruct;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.coury.jfilehelpers.engines.FileHelperEngine;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import com.btpn.custody.common.Constant;
import com.btpn.custody.dao.IAddress;
import com.btpn.custody.dao.IMasterData;
import com.btpn.custody.entity.Address;
import com.btpn.custody.entity.CIF;
import com.btpn.custody.entity.MasterData;
import com.btpn.custody.service.IMasterService;
import com.btpn.custody.web.m006.fixlength.FL_Address;
import com.btpn.custody.web.m006.fixlength.FL_Individu;
import com.btpn.custody.web.m006.fixlength.FL_NonIndividu;
import com.btpn.custody.web.m006.model.ResponseCIFAddress;
import com.btpn.infra.utils.MyDateUtils;
import com.btpn.infra.utils.MySFTPclient;

/*
 * PURPOSE : upload to MD with status=approved/failUpload
 * USER : admin
 */
@Component("dailyEQ")
public class DailyEQ {

	private final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(this.getClass());
	
	@Autowired
	private IAddress addressDaoHibernate;
	
	@Autowired
	private IMasterData masterDataDaoHibernate;

	@Autowired
	private IMasterService masterDataService;
	
//	private Properties prop;
	private Thread upDownThread;
//	private Thread response;
//	private Boolean isAllowSend=true;
//	private Boolean isAllowRead=true;
	@Value("${SCHEDULER_UPLOAD_TO_EQ}")
	private Long upDownMin;
//	private Long downloadInMin;
		
	@Value("${PATH_UPLOAD_TO_EQ}")
	private String logUploadPath;
	
	@PostConstruct
	public void initDailyEQ() {
		
		// Creates a Scheduler instance (i use cron4j)
//		Scheduler upload = new Scheduler();
//		upload.schedule(prop.getProperty("SCHEDULER_UPLOAD_TO_EQ"), new Runnable() {
//			public void run() {
//				if (isAllowSend) {
//					sendDailyToEQ(Constant.USER_SYSTEM);
//				} else {
//					logger.info("UPLOAD EQ : START [still run previous process]");
//				}
//			}
//		});
//		upload.start();
//		
//		Scheduler response = new Scheduler();
//		response.schedule(prop.getProperty("SCHEDULER_DOWNLOAD_FROM_EQ"), new Runnable() {
//			public void run() {
//				if (isAllowRead) {
//					getResponseEQ(Constant.USER_SYSTEM);
//				} else {
//					logger.info("GET RESPONSE : START [still run previous process]");
//				}
//			}
//		});
//		response.start();
		
		/*
		 * uploadEQ
		 * sleep 1minute
		 * getResponse
		 * sleep 30minutes
		 */
		upDownThread = new Thread() {
			public void run() {		
//				try {
					while(true) {
												
						Long start=0L, end=0L;
	//					if (isAllowSend) {
							// clear EQ output folder
							MySFTPclient.removeFilenames(MySFTPclient.getFilenames());
						
							start = System.currentTimeMillis();
							String hasil = sendDailyToEQ(Constant.USER_SYSTEM);
							end = System.currentTimeMillis();
							logger.info("PROCESS UPLOAD EQ TIME : {} sec", TimeUnit.MILLISECONDS.toSeconds(end-start));
								
							if (hasil!=null) {
								
								if (hasil==Constant.STATUS__SUCCESS) {									
									int count=0;
									
									// wait for response CMC & CMD
									while (!isCmcAndCmdExist()) {
										try {
											logger.info("SLEEP for 3 minute");
											Thread.sleep(180000);
										} catch (InterruptedException e) {
											logger.error(e.getMessage());
										}
										count++;
										
										if (count==120) {	// max 120 minutes
											break;
										}
									}
									
									if (count!=120) {
										start = System.currentTimeMillis();
										getResponseEQ(Constant.USER_SYSTEM);
										end = System.currentTimeMillis();
										logger.info("PROCESS GET RESPONSE EQ TIME : {} sec", TimeUnit.MILLISECONDS.toSeconds(end-start));
									} else {
										// clear EQ output folder
										MySFTPclient.removeFilenames(MySFTPclient.getFilenames());
									}
								} else {
									logger.info("NOT GET RESPONSE: " + hasil);
								}
							}
	//					} else {
	//						logger.info("UPLOAD EQ : START [still run previous process]");
	//					}
						
						try {
							logger.info("SLEEP for {} minutes", upDownMin);
							Thread.sleep(upDownMin * 60 * 1000);
						} catch (InterruptedException e) {
							logger.error(e.getMessage());
						}
					}
//				} catch (Exception ex) {
//					logger.error("Thread Exception : "+ex.getMessage());
//					try {
//						Thread.sleep(upDownMin * 60 * 1000);
//					} catch (InterruptedException e) {
//						logger.error(e.getMessage());
//					}
//				}
			}
		};
		upDownThread.start();
		
//		response = new Thread() {
//			public void run() {						
//				while(true) {
//					Long start=0L, end=0L;
////					if (isAllowRead) {
//						start = System.currentTimeMillis();
//						getResponseEQ(Constant.USER_SYSTEM);
//						end = System.currentTimeMillis();
////					} else {
////						logger.info("GET RESPONSE : START [still run previous process]");						
////					}
//					logger.info("GET RESPONSE TIME : " + ((end - start) / 1000) + " sec");
//					try {
//						Thread.sleep(downloadInMin * 1000);
//					} catch (InterruptedException e) {
//						logger.error(e.getMessage());
//					}
//				}
//			}
//		};
//		response.start();
	}

	/*
	 * Upload to EQ
	 * Run by scheduler/manual
	 */
	public String sendDailyToEQ(String user) {

		// get MD by STATUS_MD=approved/failUpload
		List<MasterData> mds = masterDataDaoHibernate.findByStatuses(Constant.STATUS__MD_APPROVED, Constant.STATUS__MD_FAIL_UPLOAD);
		if (mds.isEmpty()) {
			logger.info("UPLOAD EQ : START [No Data to Upload]");
			return null;
		}
		
		logger.info("UPLOAD EQ : START ({})", mds.size());
//		isAllowSend=false;
		
		// init CMA(ind, non) & CMB(address) generator
		FileHelperEngine<FL_Individu> cmaInd = new FileHelperEngine<FL_Individu>(FL_Individu.class);
		FileHelperEngine<FL_NonIndividu> cmaNonInd = new FileHelperEngine<FL_NonIndividu>(FL_NonIndividu.class);
		FileHelperEngine<FL_Address> cmb = new FileHelperEngine<FL_Address>(FL_Address.class);
		
		// init list individu & non-individu
		List<FL_Individu> listOfInd = new ArrayList<FL_Individu>();
		List<FL_NonIndividu> listOfNonInd = new ArrayList<FL_NonIndividu>();
		List<FL_Address> listOfAddress = new ArrayList<FL_Address>();
		
		// add to list
		for (MasterData md : mds) {			
			
			// CMA
			if (StringUtils.contains(Constant.CST_INDIVIDU, md.getCIF().getCUSTOMER_TYPE())) {
				listOfInd.add(createIndividu(md.getCIF()));
			} else {
				listOfNonInd.add(createNonIndividu(md.getCIF()));
			}
			
			// CMB
			List<Address> addresses = addressDaoHibernate.findByMD(md.getSid());
			if (addresses.size()>0) {
				for (Address a : addresses) {
					if (a != null) {
						String concat = StringUtils.trimToEmpty(a.getADDRESS1())
										.concat(StringUtils.trimToEmpty(a.getADDRESS2()))
										.concat(StringUtils.trimToEmpty(a.getADDRESS3()))
										.concat(StringUtils.trimToEmpty(a.getADDRESS4()))
										.concat(StringUtils.trimToEmpty(a.getADDRESS5()));
						if (concat.length()>0) {
							logger.debug(md.getSid()+" : " +concat);
							listOfAddress.add(createAddress(a, md.getCIF().getCifSid()));
						}
					}						
				}
			}
		}
		
		// generate CMA (for CMB langsung)
		StringBuffer sbCMA = new StringBuffer();
		sbCMA.append(cmaInd.getResult(listOfInd, -1));
		sbCMA.append(cmaNonInd.getResult(listOfNonInd, -1));

		String filenameCMA = MySFTPclient.generateFilename(Constant.SUFFIX_CMA);
		String filenameCMB = MySFTPclient.generateFilename(Constant.SUFFIX_CMB);
		String contentCMA = sbCMA.toString();
		String contentCMB = cmb.getResult(listOfAddress, -1);
		String resultCMA = Constant.STATUS__FAIL;
		String resultCMB = Constant.STATUS__FAIL;
				
		try {
			// keep file
			writeLogFile(filenameCMA, contentCMA);
			writeLogFile(filenameCMB, contentCMB);
			
			// send CMA & CMB
			resultCMA = MySFTPclient.putEQ(IOUtils.toInputStream(contentCMA), filenameCMA);
			resultCMB = MySFTPclient.putEQ(IOUtils.toInputStream(contentCMB), filenameCMB);
		} catch (IOException e) {
			logger.error("WRITE LOG EQ : FAIL", e);
		}				
		
		if ((resultCMA.contains(Constant.STATUS__SUCCESS)) & (resultCMB.contains(Constant.STATUS__SUCCESS))) {
			
			// set send EQ date (next : maybe can be set uploaded status)
			for (MasterData md : mds) {
				md.setSEND_EQ_DATE(new Date());
				masterDataDaoHibernate.update(md);
			}

			logger.info("UPLOAD EQ : CMA & CMB SUCCESS");
			mds.clear();mds=null;sbCMA=null;
//			isAllowSend=true;
			return Constant.STATUS__SUCCESS;
		} else {
			logger.info("UPLOAD EQ : FAIL [caused: {} or {}]", resultCMA, resultCMB);
			mds.clear();mds=null;sbCMA=null;
//			isAllowSend=true;
			return resultCMA + Constant.NEW_LINE + resultCMB;
		}
	}
	
	// write file
	private void writeLogFile(String filename, String content) throws IOException {
		File folder = new File(logUploadPath);
		File file = new File(logUploadPath + filename);
		
        //if file doesnt exists, then create it
		if (!folder.exists()) {
			folder.mkdir();
		} 
		file.createNewFile();

        // with FileOutputStream
        FileOutputStream fop = new FileOutputStream(file);
        fop.write(content.getBytes());
        fop.flush();
        fop.close();
	}
	
	/*
	 * get response from EQ for CMC and CMD
	 * update STATUS_MD
	 * then delete file
	 */
	public String getResponseEQ(String user) {
		
		// get list of filenames
		List<String> filenames = MySFTPclient.getFilenames();
		if (filenames.isEmpty()) {
			logger.info("GET RESPONSE : START [Empty File]");
			return null;
		}
		
		logger.info("GET RESPONSE : START ({} files)", filenames.size());
//		isAllowRead=false;
		
		Map<String, ResponseCIFAddress> responseMap = new HashMap<String, ResponseCIFAddress>();
		for (String filename : filenames) {
			
			logger.info("READ File : {}", filename);
			
			InputStream is = MySFTPclient.getResponseEQ(filename);
			if (is != null) {						
				try {
					InputStreamReader isr = new InputStreamReader(is);
					BufferedReader br = new BufferedReader(isr);
					String read = br.readLine();
		
					if (filename.contains(Constant.SUFFIX_CMC)) {											
						while(read != null) {							
							ResponseCIFAddress response = responseMap.get(read.substring(0, 6));
							if (response == null) {
								response = new ResponseCIFAddress();
//								response.setCif(read.substring(0, 6));
								if (read.length()==6) {		// success
									response.setStatusCif(true);
									response.setStatusAddress(false);	// init
								} else {					// fail
									response.setStatusCif(false);
									response.setErrorMsgCif(read.substring(0));		// error msg
									response.setStatusAddress(false);	// init
								}								
								responseMap.put(read.substring(0, 6), response);
							} else {
								ResponseCIFAddress newResponse = new ResponseCIFAddress();
//								newResponse.setCif(response.getCif());
								newResponse.setStatusAddress(response.getStatusAddress());
								newResponse.setErrorMsgAddress(response.getErrorMsgAddress());
								if (read.length()==6) {		// success
									newResponse.setStatusCif(true);
								} else {					// fail
									newResponse.setStatusCif(false);
									newResponse.setErrorMsgCif(read.substring(0));
								}
								responseMap.put(read.substring(0, 6), newResponse);
							}
							
							read = br.readLine();
	//						System.out.println(read);
						}
					} else if (filename.contains(Constant.SUFFIX_CMD)) {											
						while(read != null) {							
							ResponseCIFAddress response = responseMap.get(read.substring(0, 6));
							if (response == null) {
								response = new ResponseCIFAddress();
//								response.setCif(read.substring(0, 6));
								if (read.length()==6) {		// success
									response.setStatusAddress(true);
									response.setStatusCif(false); 	// init
								} else {					// fail
									response.setStatusAddress(false);
									response.setErrorMsgAddress(read.substring(0));
									response.setStatusCif(false); 	// init
								}								
								responseMap.put(read.substring(0, 6), response);
							} else {
								ResponseCIFAddress newResponse = new ResponseCIFAddress();
//								newResponse.setCif(response.getCif());
								newResponse.setStatusCif(response.getStatusCif());
								newResponse.setErrorMsgCif(response.getErrorMsgCif());
								if (read.length()==6) {		// success
									newResponse.setStatusAddress(true);
								} else {					// fail
									newResponse.setStatusAddress(false);
									newResponse.setErrorMsgAddress(read.substring(0));
								}
								responseMap.put(read.substring(0, 6), newResponse);
							}
							
							read = br.readLine();
	//						System.out.println(read);
						}
					}
					isr.close();isr=null;
					br.close();br=null;
				} catch (IOException e) {
					logger.error("GET RESPONSE : FAIL", e);
//					isAllowRead=true;
					return Constant.STATUS__FAIL;
				} finally {
					if (is!=null) {
						try {
							is.close();is=null;
						} catch (IOException e) {
							logger.error("GET RESPONSE : FAIL", e);
//							isAllowRead=true;
							return Constant.STATUS__FAIL;
						}
					}
				}
			}
//			if (is!=null) {
//				try {
//					is.close();
//				} catch (IOException e) {
//					// TODO Auto-generated catch block
//					e.printStackTrace();
//				}
//			}
		}
		
		// update
		for(Map.Entry<String, ResponseCIFAddress> entry : responseMap.entrySet()) {

//			ResponseCIFAddress res = entry.getValue();
//			System.out.println(res.getStatusCif() + "-"+res.getErrorMsgCif());
//			System.out.println(res.getStatusAddress() + ":" + res.getErrorMsgAddress());
			ResponseCIFAddress res = entry.getValue();
			MasterData md = masterDataDaoHibernate.getByCif(entry.getKey());
			if (md != null) {
				if (res.getStatusCif() & res.getStatusAddress()) {
					 
					// set as FAIL_UPLOAD_REASON as date
					md.setFAIL_UPLOAD_REASON(MyDateUtils.dateToStr(Constant.FORMAT_PRESENTATION, new Date()));
					md.setUpdatedDate(new Date());
					md.setUpdatedBy(Constant.USER_SYSTEM);
					masterDataDaoHibernate.update(md);
					masterDataService.updateStatus(md.getSid(), Constant.STATUS__MD_RESPONSED, user);
				} else {
					md.setFAIL_UPLOAD_REASON(entry.getKey() + " -> [" + StringUtils.trimToEmpty(res.getErrorMsgCif()) + "] [" + StringUtils.trimToEmpty(res.getErrorMsgAddress()) + "]");
					md.setUpdatedDate(new Date());
					md.setUpdatedBy(Constant.USER_SYSTEM);
					masterDataDaoHibernate.update(md);
					masterDataService.updateStatus(md.getSid(), Constant.STATUS__MD_FAIL_UPLOAD, user);
				}
			}
		}
		
		// remove files
		MySFTPclient.removeFilenames(filenames);	
		
		logger.info("GET RESPONSE : FINISH");
//		isAllowRead=true;
		return Constant.STATUS__SUCCESS;
	}
	 
	// Get response from OUTPUT, is CMC & CMD exist ONLY couple
	private boolean isCmcAndCmdExist() {
		int cmc = 0;
		int cmd = 0;
		for (String filename : MySFTPclient.getFilenames()) {
			if (filename.contains(Constant.SUFFIX_CMC)) cmc++;
			if (filename.contains(Constant.SUFFIX_CMD)) cmd++;			
		}
		if ((cmc>0) & (cmd>0)) return true;
		return false;
	}
	
	private FL_Individu createIndividu(CIF cif) {
		return new FL_Individu(
				cif.getCifSid(), 
				"", 
				cif.getCUST_FULL_NAME(), 
				cif.getPARENT_COUNTRY(), 
				cif.getGENDER(), 
				cif.getID_TYPE(), 
				cif.getID_NUMBER(), 
				cif.getID_EXPIRED(),
				cif.getBIRTH_DATE(),
				cif.getPLACE_OF_BIRTH(), 
				cif.getNPWP(), 
				cif.getRELIGION(), 
				cif.getEDUCATION(), 
				cif.getCURRENT_INCOME(), 
				cif.getSOURCE_INCOME(), 
				cif.getPURPOSE(), 
				cif.getPROFESSION(), 
				cif.getJABATAN(), 
				StringUtils.leftPad(cif.getFREQ_TRX_DB(), 5, "0"),
				cif.getJENIS_USAHA(), 
				StringUtils.leftPad(cif.getFREQ_TRX_CR(), 5, "0"),
				cif.getMARITAL_STATUS(), 
				StringUtils.leftPad(cif.getEST_TRX_NORMAL_DB(), 15, "0"),
				cif.getNAMA_AHLI_WARIS(), 
				cif.getALAMAT_AHLI_WARIS1(), 
				cif.getALAMAT_AHLI_WARIS2(), 
				cif.getKOTA_AHLI_WARIS(), 
				cif.getHUB_AHLI_WARIS(), 
				cif.getREK_BANK_LAIN1(), 
				cif.getREK_BANK_LAIN2(), 
				cif.getREK_BANK_LAIN3(), 
				StringUtils.leftPad(cif.getSALDO_NORMAL(), 15, "0"), 
				cif.getMOTHER_NAME(), 
				StringUtils.leftPad(cif.getEST_TRX_NORMAL_CR(), 15, "0"),
				cif.getLBU_GOL_NASABAH(), 
				cif.getHR_CUSTOMER(), 
				cif.getHR_BUSINESS(), 
				cif.getHR_COUNTRIES(), 
				cif.getHR_PRODUCT(), 
				cif.getADDITIONAL_INCOME());
	}
	
	private FL_NonIndividu createNonIndividu(CIF cif) {
		return new FL_NonIndividu(
				cif.getCifSid(), 
				"", 
				cif.getCUST_FULL_NAME(), 
				cif.getPARENT_COUNTRY(), 
				cif.getINFO_AKTE_PENDIRIAN(), 
				cif.getINFO_AKTE_PENDIRIAN2(), 
				cif.getAKTE_TERAKHIR_TANGGAL1(), 
				cif.getAKTE_TERAKHIR_TANGGAL2(), 
				cif.getSIUP(), 
				cif.getTANDA_DAFTAR_PERUSAHAAN(), 
				MyDateUtils.dateToStr(Constant.FORMAT_EQ_EXPORT, cif.getTGL_AKHIR_TDP()),	// its optional, i'll handle for NULL
				cif.getNPWP(), 
				cif.getCURRENT_INCOME(), 
				cif.getREK_BANK_LAIN1(), 
				cif.getREK_BANK_LAIN2(), 
				cif.getREK_BANK_LAIN3(), 
				cif.getLAP_KEUANGAN(), 
				cif.getOPEN_DATE(),
				cif.getJENIS_USAHA(), 
				cif.getPURPOSE(), 
				cif.getHUB_DENGAN_BANK(), 
				cif.getLBU_GOL_NASABAH(), 
				StringUtils.leftPad(cif.getFREQ_TRX_DB(), 5, "0"),
				StringUtils.leftPad(cif.getFREQ_TRX_CR(), 5, "0"),
				StringUtils.leftPad(cif.getEST_TRX_NORMAL_DB(), 15, "0"),
				StringUtils.leftPad(cif.getEST_TRX_NORMAL_CR(), 15, "0"),
				cif.getSOURCE_INCOME(), 
				cif.getHR_CUSTOMER(), 
				cif.getHR_BUSINESS(), 
				cif.getHR_COUNTRIES(), 
				cif.getHR_PRODUCT(),
				cif.getADDITIONAL_INCOME());
	}
	
	private FL_Address createAddress(Address address, String cif) {
		
		// if prime address then BLANK
		if (address.getADDRESS_TYPE().equals(Address.PRIME_ADDRESS)) {
			return new FL_Address(
					cif, 
					"", 
					" ", 
					address.getADDRESS1(), 
					address.getADDRESS2(), 
					address.getADDRESS3(), 
					address.getADDRESS4(), 
					address.getADDRESS5(), 
					address.getPOST_CODE());
		} else {
			return new FL_Address(
					cif, 
					"", 
					address.getADDRESS_TYPE(), 
					address.getADDRESS1(), 
					address.getADDRESS2(), 
					address.getADDRESS3(), 
					address.getADDRESS4(), 
					address.getADDRESS5(), 
					address.getPOST_CODE());
		}
	}
	
//	public static void main(String[] args) {
//		
//			File file = new File("C:\\before.txt");
//			FileInputStream fis = null;
//		    BufferedInputStream bis = null;
//		    DataInputStream dis = null;
//		    StringBuffer sb = new StringBuffer();
//		    try {
//		      fis = new FileInputStream(file);
//
//		      // Here BufferedInputStream is added for fast reading.
//		      bis = new BufferedInputStream(fis);
//		      dis = new DataInputStream(bis);
//
//		      // dis.available() returns 0 if the file does not have more lines.
//		      while (dis.available() != 0) {
//
//		      // this statement reads the line from the file and print it to
//		        // the console.
////		        System.out.println(dis.readLine());
//		    	  sb.append(dis.readLine());
//		      }
//
//		      // dispose all the resources after using them.
//		      fis.close();
//		      bis.close();
//		      dis.close();
//
//		    } catch (FileNotFoundException e) {
//		      e.printStackTrace();
//		    } catch (IOException e) {
//		      e.printStackTrace();
//		    }
//		String resultCMA = MySFTPclient.putEQ(IOUtils.toInputStream(sb.toString()), "before");
//		System.out.println(resultCMA);
//	}
}
